package edu.ncsu.csc.ip.client.protocol;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
import java.util.List;

import edu.ncsu.csc.ip.client.ActiveClientIndex;
import edu.ncsu.csc.ip.client.ClientInfo;
import edu.ncsu.csc.ip.client.RFCBean;
import edu.ncsu.csc.ip.client.RFCIndex;
import edu.ncsu.csc.ip.common.HeaderBean;
import edu.ncsu.csc.ip.common.Log;
import edu.ncsu.csc.ip.common.NetworkUtils;
import edu.ncsu.csc.ip.common.Protocol;
import edu.ncsu.csc.ip.common.serializer.BeanSerializer;
import edu.ncsu.csc.ip.server.ClientBean;

/**
 * Class <code></code>
 * 
 * 
 * 
 * @author Moin Ayazifar <mayazif@ncsu.edu>
 * @author Steven Elliott <sdelliot@ncsu.edu>
 * 
 */

public class ProtocolClientImpl implements Protocol {

	private State state = State.IDLE;

	private Socket socket;
	private ClientInfo clientInfo;
	private RFCIndex rfcIndex;
	private ActiveClientIndex activeClientIndex;

	public ProtocolClientImpl(Socket socket, ClientInfo clientInfo,
			RFCIndex rfcIndex, ActiveClientIndex activeClientIndex) {
		this.socket = socket;
		this.clientInfo = clientInfo;
		this.rfcIndex = rfcIndex;
		this.activeClientIndex = activeClientIndex;
	}

	public ProtocolClientImpl() {

	}

	/** THE MESSAGE REGISTER */
	public static final String REGISTER = "REGISTER";
	public static final String LEAVE = "LEAVE";
	public static final String PQUERY = "PQUERY";
	public static final String KEEPALIVE = "KEEPALIVE";
	public static final String OK = "OK";
	public static final String ERROR = "ERROR";
	public static final String GETRFC = "GETRFC-FILE";
	public static final String RFCQUERY = "GETRFC-INDEX";

	/**
	 * @param cookie
	 *            the cookie to set
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */

	public String parseCommand(String message) {
		if (message.toUpperCase().startsWith(REGISTER)) {
			return sendRegister();
		} else if (message.toUpperCase().startsWith(LEAVE)) {
			return sendLeave();
		} else if (message.toUpperCase().startsWith(PQUERY)) {
			return sendPQuery(PQUERY);
		} else if (message.toUpperCase().startsWith(KEEPALIVE)) {
			return sendKeepAlive();
		} else if (message.toUpperCase().startsWith(RFCQUERY)) {
			return rfcQuery(RFCQUERY);
		} else if (message.toUpperCase().startsWith(GETRFC)) {
			return getRFC(message);
		} else {
			return null;
		}
	}

	/**
	 * @param cookie
	 *            the cookie to set
	 */

	public String parseMessage(String message) {
		if (message.toUpperCase().startsWith(PQUERY)) {
			return receivePQuery(message);
		} else if (message.toUpperCase().startsWith(GETRFC)) {
			return receiveRfcFile(message);
		} else if (message.toUpperCase().startsWith(RFCQUERY)) {
			return receiveRfcIndex(message);
		} else if (message.toUpperCase().startsWith(OK)) {
			return receiveOK(message);
		} else if (message.toUpperCase().startsWith(ERROR)) {
			return receiveError(message);
		} else {
			return null;
		}
	}

	/**
	 * @param cookie
	 *            the cookie to set
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */

	public String sendRegister() {
		BeanSerializer<ClientBean> bSerializer = new BeanSerializer<ClientBean>(
				ClientBean.class);
		ClientBean cb = new ClientBean();
		cb.setRfcServerPort(clientInfo.getRfcServerPort());
		cb.setCookie(clientInfo.getCookie());
		state = State.SENT_REGISTERED;

		return getHeader(REGISTER) + "\n" + bSerializer.getString(cb);
	}

	/**
	 * @param cookie
	 *            the cookie to set
	 */

	public String sendLeave() {

		if (clientInfo.getCookie() != null) {
			BeanSerializer<ClientBean> bSerializer = new BeanSerializer<ClientBean>(
					ClientBean.class);
			ClientBean cb = new ClientBean();
			cb.setRfcServerPort(clientInfo.getRfcServerPort());
			cb.setCookie(clientInfo.getCookie());
			state = State.SENT_LEAVE;
			return getHeader(LEAVE) + "\n" + bSerializer.getString(cb);
		} else {
			Log.println(clientInfo + "Unregistered client cannot leave.");
			return null;
		}
	}

	/**
	 * @param state
	 *            the state to set
	 */
	public String sendKeepAlive() {
		if (clientInfo.getCookie() != null) {
			BeanSerializer<ClientBean> bSerializer = new BeanSerializer<ClientBean>(
					ClientBean.class);
			ClientBean cb = new ClientBean();
			cb.setRfcServerPort(clientInfo.getRfcServerPort());
			cb.setCookie(clientInfo.getCookie());
			state = State.SENT_KEAPALIVE;
			return getHeader(KEEPALIVE) + "\n" + bSerializer.getString(cb);
		} else {
			Log.println(clientInfo
					+ "Unregistered client cannot send KeepAlive command.");
			return null;
		}
	}

	/**
	 * @return the state
	 */
	public String sendPQuery(String message) {
		if (clientInfo.getCookie() != null) {
			BeanSerializer<ClientBean> bSerializer = new BeanSerializer<ClientBean>(
					ClientBean.class);
			ClientBean cb = new ClientBean();
			cb.setRfcServerPort(clientInfo.getRfcServerPort());
			cb.setCookie(clientInfo.getCookie());
			state = State.SENT_PCQUERY;
			return getHeader(PQUERY) + "\n" + bSerializer.getString(cb);
		} else {
			Log.println(clientInfo
					+ "Unregistered client cannot send Peer Query command.");
			return null;
		}
	}

	/**
	 * @return the state
	 */
	public String receivePQuery(String message) {
		String[] splitMessages = message.split(HEADER_SPLITTER);
		state = State.IDLE;
		try {
			BeanSerializer<ClientBean> bSerializer = new BeanSerializer<ClientBean>(
					ClientBean.class);

			List<ClientBean> clientBeanList = bSerializer
					.getAllBean(splitMessages[1]);
			activeClientIndex.addClients(clientBeanList);
			Log.println(clientInfo + "Peer List Receive Successfully. Count:"
					+ clientBeanList.size());
			rfcIndex.removeInactiveRFCBeans(clientBeanList);
			return null;
		} catch (Exception e) {
			activeClientIndex.addClients(null);
			rfcIndex.removeInactiveRFCBeans(null);
			Log.println(clientInfo + "No Peer List Received: '" + message + "'");
			return null;
		}
	}

	/**
	 * @param message
	 * 
	 */

	public String receiveOK(String message) {
		if (state == State.SENT_REGISTERED) {
			String[] splitMessages = message.split(HEADER_SPLITTER);
			state = State.IDLE;
			try {
				BeanSerializer<ClientBean> bSerializer = new BeanSerializer<ClientBean>(
						ClientBean.class);
				ClientBean clientBean = bSerializer.getBean(splitMessages[1]);
				clientInfo.setCookie(clientBean.getCookie());
				clientInfo.setLocalAddress(NetworkUtils.getLocalAdress(socket)
						.getHostAddress());
				clientInfo.setLocalHostName(NetworkUtils.getLocalAdress(socket)
						.getHostName());
				Log.println(clientInfo
						+ "Client successfully registered with Cookie: '"
						+ clientBean.getCookie() + "'");
				return null;
			} catch (Exception e) {
				Log.println(clientInfo
						+ "Client not resgisted. OK Message is not well-formed: '"
						+ message + "'");
				return null;
			}

		} else if (state == State.SENT_LEAVE) {
			String[] splitMessages = message.split(HEADER_SPLITTER);
			state = State.IDLE;
			try {
				clientInfo.setCookie(null);
				Log.println(clientInfo + splitMessages[1]);
				return null;
			} catch (Exception e) {
				Log.println(clientInfo
						+ "Client did not leave RS. OK Message is not well-formed: '"
						+ message + "'");
				return null;
			}
		} else if (state == State.SENT_KEAPALIVE) {
			String[] splitMessages = message.split(HEADER_SPLITTER);
			state = State.IDLE;
			if (splitMessages.length == 0) {
				Log.println(clientInfo
						+ "Client TTL was not renewed. OK Message is not well-formed: '"
						+ message + "'");

			}
			return null;
		} else {
			return null;
		}
	}

	/**
	 * @param message
	 * 
	 */

	public String receiveError(String message) {

		String[] splitMessages = message.split(HEADER_SPLITTER);
		state = State.IDLE;
		try {
			Log.println(clientInfo + "Error Message Reseived:"
					+ splitMessages[1]);
			return null;
		} catch (Exception e) {
			Log.println(clientInfo + "Error Message is not well-formed: '"
					+ message + "'");
			return null;
		}

	}

	/**
	 * @param state
	 *            the state to set
	 */
	public String rfcQuery(String message) {
		if (clientInfo.getCookie() != null) {
			state = State.SENT_RFCQUERY;
			return getHeader(RFCQUERY) + "\n";
		} else {
			Log.println(clientInfo
					+ "Unregistered client cannot send RFC Index Query command.");
			return null;
		}
	}

	/**
	 * @param state
	 *            the state to set
	 */
	public String getRFC(String message) {
		try {
			String param = message.split(" ")[1].trim();
			if (clientInfo.getCookie() != null) {
				state = State.SENT_GETRFC;
				return getHeader(GETRFC, param) + "\n";
			} else {
				Log.println(clientInfo
						+ "Unregistered client cannot request RFC file.");
				return null;
			}
		} catch (Exception e) {
			Log.println(clientInfo + "Command is not well-formed. Example: ["
					+ GETRFC + " 1234]");
			return null;
		}
	}

	public String receiveRfcFile(String message) {
		String[] splitMessages = message.split(HEADER_SPLITTER);
		state = State.IDLE;
		try {
			HeaderBean headerBean = getHeaderBean(splitMessages[0]);

			File file = new File(rfcIndex.getRFCFileName(headerBean
					.getParameter()));
			PrintWriter printWriter = new PrintWriter(new FileWriter(file,
					false));
			int lineNumber = 0;
			for (String line : splitMessages)
				if (lineNumber++ > 0)
					printWriter.println(line);

			printWriter.close();
			return getHeader(OK) + "\n";

		} catch (Exception e) {
			Log.println(clientInfo + "No RFC file Received. '" + message + "'");
			return null;
		}
	}

	public String receiveRfcIndex(String message) {
		String[] splitMessages = message.split(HEADER_SPLITTER);
		state = State.IDLE;
		try {
			BeanSerializer<RFCBean> rfcSerializer = new BeanSerializer<RFCBean>(
					RFCBean.class);
			List<RFCBean> rfcBeanList = rfcSerializer
					.getAllBean(splitMessages[1]);
			rfcIndex.addAllGlobalRFC(rfcBeanList);
			Log.println(clientInfo + "RFC Index Receive Successfully. Count:"
					+ rfcBeanList.size());
			return null;
		} catch (Exception e) {
			Log.println(clientInfo + "No RFC Bean Received. '" + message + "'");
			return null;
		}
	}

	public String parseRFCMessage(String message) {
		String[] messageArray = message.split(" ");
		return messageArray[1];
	}

	private String getHeader(String message) {

		return getHeader(message, null);

	}

	private String getHeader(String message, String parameter) {

		HeaderBean headerBean = new HeaderBean();
		headerBean.setParameter(parameter);
		headerBean.setMessage(message);
		headerBean.setOs(NetworkUtils.getOsName());
		headerBean.setProtocol(PROTOCOL_VERSION);
		headerBean.setDate(new Date());
		headerBean.setAddress(NetworkUtils.getLocalAdress(socket)
				.getHostAddress());
		headerBean.setHostname(NetworkUtils.getLocalAdress(socket)
				.getHostName());
		BeanSerializer<HeaderBean> bSerializer = new BeanSerializer<HeaderBean>(
				HeaderBean.class);
		String headerString = bSerializer.getString(headerBean);
		String headerMessage = headerString.substring(HeaderBean.TRIM_STRING
				.length());
		return headerMessage;

	}

	/**
	 * @return the socket
	 */
	@Override
	public Socket getSocket() {
		return socket;
	}

	/**
	 * @param socket
	 *            the socket to set
	 */
	@Override
	public void setSocket(Socket socket) {
		this.socket = socket;
	}

	@Override
	public boolean isRFCCommand(String command) {
		if (command.toUpperCase().startsWith(RFCQUERY)
				|| command.toUpperCase().startsWith(GETRFC))
			return true;
		return false;
	}

	@Override
	public boolean isFileTransferCommand(String command) {
		if (command.toUpperCase().startsWith(GETRFC))
			return true;
		return false;
	}

	HeaderBean getHeaderBean(String headerMessage) {

		String headerString = HeaderBean.TRIM_STRING + headerMessage;
		BeanSerializer<HeaderBean> bSerializer = new BeanSerializer<HeaderBean>(
				HeaderBean.class);
		return bSerializer.getBean(headerString);

	}

}
